﻿using Levfier.GUI.EditorControls.Exceptions;
using Levfier.Proccessors.Chains;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;

namespace Levfier.GUI.EditorControls.Chains
{
    /// <summary>
    /// バッファーチェインに対応するGUIパーツ
    /// </summary>
    [Serializable]
    public class BufferChainControl : LinkControl, IChainControl
    {
        #region 定数

        /// <summary>
        /// 通常時の色
        /// </summary>
        private readonly Brush normalBrush = Brushes.White;

        #endregion

        /// <summary>
        /// 通過した回数
        /// </summary>
        public int PassedThroughCount { get; set; }

        /// <summary>
        /// 一処理のサンプル数
        /// </summary>
        protected int sampleCount;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="upper">上流のプロセッサコントロール</param>
        /// <param name="lower">下流のプロセッサコントロール</param>
        /// <exception cref="RouteException">接続に失敗した場合発生</exception>
        public BufferChainControl(ProccessorControl upper, ProccessorControl lower, int sampleCount)
            : base(upper, lower)
        {
            this.sampleCount = sampleCount;
            this.lineCreater = new ChainLineCreater();
            this.lineCreater.Stroke = this.normalBrush;

            try
            {
                //上流→チェイン 接続する
                upper.SetLowerChainControl(this);
                //チェイン→下流 接続する
                lower.SetUpperChainControl(this);
                //ルートチェックを行う
                upper.CheckRoute();
            }
            catch (RouteException ex)
            {
                //ループ発生時 or 接続方向不正
                upper.RemoveLowerChainControl(this);
                lower.RemoveUpperChainControl(this);
                throw ex;
            }
            finally
            {
                //ルートチェックを初期化する
                upper.InitRouteCheck();
                lower.InitRouteCheck();

                //描画処理
                this.lineCreater = new ChainLineCreater();
                this.lineCreater.Stroke = Brushes.White;
                this.Update();
            }
        }

        /// <summary>
        /// プロセッサ接続に対応するGUIパーツ
        /// 継承先の子クラスで実装のこと
        /// </summary>
        /// <returns>プロセッサ接続</returns>
        public IChainable GetChain()
        {
            if (null == this.chain)
            {
                this.chain = new BufferChain(this.sampleCount);
            }
            return this.chain;
        }

        /// <summary>
        /// チェインを設定する
        /// </summary>
        /// <param name="chain">チェイン</param>
        public void SetChain(IChainable chain)
        {
            this.chain = chain;
        }

        /// <summary>
        /// ルートをチェックする
        /// </summary>
        /// <exception cref="RouteLoopException">ループが発生した場合に発生する</exception>
        /// <returns>出力まで到達できなかった末端経路数</returns>
        public int CheckRoute()
        {
            //通過回数をインクリメント
            this.PassedThroughCount++;

            //通過した回数が上流チェインの数より多い場合、ループと判定
            if (1 < this.PassedThroughCount)
            {
                //画面でキャッチすること
                //ここに入ったら実装ミス
                throw new RouteLoopException("ループ発生", null);
            }

            //下流が設定されていない場合
            if (null == this.lower)
            {
                //ここに入ったら実装ミス
                return 1;
            }

            return (this.lower as ProccessorControl).CheckRoute();
        }

        // <summary>
        /// ルートチェックを初期化する
        /// </summary>
        public void InitRouteCheck()
        {
            if (null != this.lower)
            {
                (this.lower as ProccessorControl).InitRouteCheck();
            }
            this.PassedThroughCount = 0;
        }

        /// <summary>
        /// 選択された時の動作
        /// </summary>
        /// <param name="sender"></param>
        public override void Select(object sender)
        {
            this.lineCreater.Stroke = this.selectedBrush;
            this.Update();
        }

        /// <summary>
        /// 選択解除された時の動作
        /// </summary>
        /// <param name="sender"></param>
        public override void Unselect(object sender)
        {
            this.lineCreater.Stroke = this.normalBrush;
            this.Update();
        }

        /// <summary>
        /// 自分自身を削除する
        /// </summary>
        public override IEnumerable<UIElement> Delete()
        {
            IList<UIElement> deleteTargets = new List<UIElement>();
            
            //画面に関する削除処理
            ProccessorControl upperPro = this.upper as ProccessorControl;
            ProccessorControl lowerPro = this.lower as ProccessorControl;
            this.Children.Clear();
            upperPro.RemoveLowerChainControl(this);
            lowerPro.RemoveUpperChainControl(this);
            this.upper = null;
            this.lower = null;
            this.chain = null;

            deleteTargets.Add(this);
            return deleteTargets;
        }
    }
}
